﻿using System;
using System.IO;
using Autofac;
using SXICE.Core.Helpers;
using SXICE.Core.Infrastructure;
using SXICE.Core.Models;

namespace SXICE.DistributedCache
{
    /// <summary>
    /// 核心类
    /// </summary>
    public class Core : IDistributedCache, IModule
    {
        /// <summary>
        /// 日期管理器
        /// </summary>
        private ILog Log => RuntimeHelper.Resolve<ILog>();

        public bool AcquireLock(string key, TimeSpan expire)
        {
            try
            {
                return RedisConnectionHelper.DB.LockTake(key, "Cache", expire);
            }
            catch (Exception ex)
            {
                Log.Error(ex);
                return false;
            }
        }

        public void AddOrUpdate<T>(string key, T obj)
        {
            Set(key, obj);
        }

        public bool Exists(string key)
        {
            try
            {
                return RedisConnectionHelper.DB.KeyExists(key);
            }
            catch (Exception ex)
            {
                Log.Error(ex);
                return false;
            }
        }

        public ModuleInfo GetModuleInfo()
        {
            return new ModuleInfo
            {
                Author = "ts",
                CreateDate = new DateTime(2020, 10, 18),
                Description = "分布式缓存",
                LastUpdateDate = new DateTime(2020, 10, 18),
                Version = "1.0"
            };
        }

        public T GetOrAdd<T>(string key, Func<T> func, TimeSpan? slidingExpiration = null, DateTime? absoluteExpiration = null)
        {
            var str = RedisConnectionHelper.DB.StringGet(key);
            if (str.IsNullOrEmpty)
            {
                var value = func();
                Set(key, value, slidingExpiration, absoluteExpiration);
                return value;
            }
            else
            {
                var cacheObj = ConvertHelper.JsonToObject<RedisSlidingEntity<T>>(str);
                //平滑时间
                if (cacheObj.Expire.HasValue)
                    RedisConnectionHelper.DB.KeyExpire(key, cacheObj.Expire);
                return cacheObj.Value;
            }
        }

        public void Init()
        {
            try
            {
                RedisConnectionHelper.Init();
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }
        }

        public void Publish(string channel, string value)
        {
            try
            {
                RedisConnectionHelper.Subscriber.Publish(channel, value);
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }
        }

        public void Register(ContainerBuilder container)
        {
            var configContent = File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "redis.json"));
            container.RegisterInstance(ConvertHelper.JsonToObject<RedisConfigs>(configContent));
            container.RegisterType<Core>().As<IDistributedCache>().SingleInstance();
        }

        public bool ReleaseLock(string key)
        {
            try
            {
                return RedisConnectionHelper.DB.LockRelease(key, "Cache");
            }
            catch (Exception ex)
            {
                Log.Error(ex);
                return false;
            }
        }

        public void Remove(string key)
        {
            try
            {
                RedisConnectionHelper.DB.KeyDelete(key);
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }
        }

        public void Set<T>(string key, T value, TimeSpan? slidingExpiration = null, DateTime? absoluteExpiration = null)
        {
            try
            {
                var cacheObj = new RedisSlidingEntity<T>
                {
                    Value = value
                };
                TimeSpan? expireTime = null;
                if (absoluteExpiration.HasValue)
                    expireTime = absoluteExpiration.Value - DateTime.Now;
                if (slidingExpiration.HasValue)
                    cacheObj.Expire = expireTime = slidingExpiration;
                RedisConnectionHelper.DB.StringSet(key, ConvertHelper.ToJson(cacheObj), expireTime);
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }
        }

        public void Start()
        {
        }

        public void Stop()
        {
        }

        public void Subscribe(string channel, Action<string, string> action)
        {
            try
            {
                RedisConnectionHelper.Subscriber.Subscribe(channel, (a, b) =>
                {
                    action(a, b);
                });
            }
            catch (Exception ex)
            {
                Log.Error(ex);
            }
        }

        public bool TryGet<T>(string key, out T obj)
        {
            obj = default;
            try
            {
                var str = RedisConnectionHelper.DB.StringGet(key);
                if (str.IsNullOrEmpty)
                    return false;
                var cacheObj = ConvertHelper.JsonToObject<RedisSlidingEntity<T>>(str);
                obj = cacheObj.Value;
                if (cacheObj.Expire.HasValue)
                    RedisConnectionHelper.DB.KeyExpire(key, cacheObj.Expire);
                return true;
            }
            catch (Exception ex)
            {
                Log.Error(ex);
                return false;
            }
        }
    }
}
